#ifndef EVOLUTION_POPULATION_HH
#define EVOLUTION_POPULATION_HH

#include <memory>
#include <vector>
#include <utility>
#include <algorithm>

#include "individual.hh"

namespace evolution {

    class Population {
    public:
        typedef std::shared_ptr<Individual> individual_t;
        typedef std::list<std::pair<individual_t, std::list<individual_t>>> species_t;

    private:
        std::vector<individual_t> current_generation;
        species_t species;

    public:
        void add_individual(individual_t individual);
        individual_t get_best_individual () { return get_best_individual_in(current_generation); }
        individual_t get_worst_individual () { return get_worst_individual_in(current_generation); }
        void calculate_fitness();
        void next_generation();
        
        std::vector<std::shared_ptr<Individual>> const& 
        get_individuals() const { return current_generation; }
        int species_count() const { return species.size(); }


    private:
        static void place_in_species(individual_t individual, species_t& species);

        template <class List>
        static individual_t get_best_individual_in(List list) {
            return *std::max_element(list.begin(), list.end(),
                    [](individual_t const& a, individual_t const& b) {
                        return a->get_fitness() < b->get_fitness();
                    });
        }

        template <class List>
        static individual_t get_worst_individual_in(List list) {
            return *std::min_element(list.begin(), list.end(),
                    [](individual_t const& a, individual_t const& b) {
                        return a->get_fitness() < b->get_fitness();
                    });
        }
    };

}

#endif
